// handler包：负责接收前端API请求，调用模型和工具函数处理业务逻辑，返回响应结果
// 本文件专注于用户认证相关接口（注册、登录），是用户进入系统的入口
package handler

import (
	"github.com/gin-gonic/gin" // Gin框架核心包，用于处理HTTP请求、参数绑定、响应返回
	"go-blog/model"
	"go-blog/util"
	"gorm.io/gorm" // GORM框架核心包，用于数据库CRUD操作
	"net/http"     // 用于定义HTTP状态码（如400、500）
	"regexp"       // 用于邮箱格式的增强验证（比默认绑定更严格）
)

// RegisterRequest 注册接口的请求参数结构体
// 用于接收前端传入的注册数据，并通过struct tag实现参数校验
type RegisterRequest struct {
	// Username：用户名，绑定JSON中的"username"字段
	// required：必填项；min=3/max=20：长度限制3-20位（防止过短或过长）
	Username string `json:"username" binding:"required,min=3,max=20"`
	// Password：密码，绑定JSON中的"password"字段
	// required：必填项；min=6/max=20：长度限制6-20位（保证安全性和易用性）
	Password string `json:"password" binding:"required,min=6,max=20"`
	// Email：邮箱，绑定JSON中的"email"字段
	// required：必填项；email：通过Gin内置校验规则验证邮箱格式（基础格式）
	Email string `json:"email" binding:"required,email"`
}

// LoginRequest 登录接口的请求参数结构体
// 用于接收前端传入的登录数据，仅需用户名和密码（核心认证信息）
type LoginRequest struct {
	// Username：用户名，绑定JSON中的"username"字段（必填）
	Username string `json:"username" binding:"required"`
	// Password：密码，绑定JSON中的"password"字段（必填）
	Password string `json:"password" binding:"required"`
}

// Register 注册接口处理器
// @Summary 用户注册
// @Tags 认证
// @Accept json
// @Produce json
// @Param request body RegisterRequest true "注册请求参数"
// @Success 200 {object} map[string]interface{} "{"code":0,"msg":"注册成功","data":{"username":"xxx"}}"
// @Failure 400 {object} map[string]interface{} "{"code":400,"msg":"参数错误或邮箱格式无效"}"
// @Failure 500 {object} map[string]interface{} "{"code":500,"msg":"服务器错误（如数据库查询、密码加密失败）"}"
// @Router /auth/register [post]
// 功能：接收用户注册请求，校验参数合法性，检查用户名/邮箱唯一性，加密密码后创建用户
// 路由：POST /api/auth/register（公开接口，无需登录）
// 参数：db *gorm.DB - 数据库连接实例（通过依赖注入传入，便于测试和复用）
// 返回：gin.HandlerFunc - Gin框架的请求处理函数（封装业务逻辑）
func Register(db *gorm.DB) gin.HandlerFunc {
	// 返回Gin的HandlerFunc，接收Gin上下文c（包含请求和响应信息）
	return func(c *gin.Context) {
		// 1. 初始化请求参数结构体，绑定前端传入的JSON数据
		// ShouldBindJSON：自动解析请求体的JSON数据到req，失败则返回参数错误
		var req RegisterRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			// 400 Bad Request：客户端参数错误（如字段缺失、格式不符）
			// code=400：业务错误码（前端可根据码值区分错误类型）
			// msg：错误详情（告知前端具体错误原因）
			c.JSON(http.StatusBadRequest, gin.H{
				"code": 400,
				"msg":  "参数错误：" + err.Error(), // 拼接错误信息（如"username为必填项"）
			})
			return // 终止后续逻辑，避免无效执行
		}

		// 2. 邮箱格式增强验证（补充Gin内置email校验的不足）
		// 内置email校验仅检查"@"，此处用正则匹配更严格的邮箱格式（如xxx@xxx.xxx）
		emailReg := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
		if !emailReg.MatchString(req.Email) {
			c.JSON(http.StatusBadRequest, gin.H{
				"code": 400,
				"msg":  "邮箱格式无效（正确格式：xxx@xxx.xxx）",
			})
			return
		}

		// 3. 检查用户名/邮箱是否已存在（避免重复注册）
		var existingUser model.User // 用于存储查询到的已存在用户
		// Where条件：查询"用户名=传入用户名"或"邮箱=传入邮箱"的用户
		// First：查询符合条件的第一条记录（若存在则说明重复）
		if err := db.Where("username = ? OR email = ?", req.Username, req.Email).First(&existingUser).Error; err != nil {
			// 处理数据库查询错误（如连接超时、SQL语法错误）
			if err != gorm.ErrRecordNotFound {
				// 500 Internal Server Error：服务器内部错误（非客户端原因）
				c.JSON(http.StatusInternalServerError, gin.H{
					"code": 500,
					"msg":  "查询失败：" + err.Error(),
				})
				return
			}
			// 若err == gorm.ErrRecordNotFound：说明用户名和邮箱均未被注册，继续后续逻辑
		} else {
			// 若查询到用户：判断是用户名重复还是邮箱重复
			if existingUser.Username == req.Username {
				c.JSON(http.StatusOK, gin.H{
					"code": 1, // 业务错误码（1表示"资源已存在"，区别于400参数错、500服务器错）
					"msg":  "用户名已存在，请更换其他用户名",
				})
				return
			}
			if existingUser.Email == req.Email {
				c.JSON(http.StatusOK, gin.H{
					"code": 1,
					"msg":  "邮箱已被注册，请更换其他邮箱",
				})
				return
			}
		}

		// 4. 密码哈希（安全存储，不存储明文密码）
		// 调用util.HashPassword：使用bcrypt算法对明文密码加密（不可逆，安全性高）
		hashPwd, err := util.HashPassword(req.Password)
		if err != nil {
			// 密码加密失败（如算法异常），返回服务器错误
			c.JSON(http.StatusInternalServerError, gin.H{
				"code": 500,
				"msg":  "密码加密失败，请重试",
			})
			return
		}

		// 5. 创建新用户（将数据写入数据库）
		// 构造User模型实例：仅传入必要字段（用户名、加密后密码、邮箱）
		newUser := model.User{
			Username: req.Username,
			Password: hashPwd, // 存储加密后的密码（明文密码丢弃）
			Email:    req.Email,
		}
		// Create：将newUser插入到数据库的t_user表（表名由model.User的TableName方法定义）
		if err := db.Create(&newUser).Error; err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"code": 500,
				"msg":  "创建用户失败：" + err.Error(),
			})
			return
		}

		// 6. 注册成功：返回成功响应（不返回敏感信息，如密码）
		c.JSON(http.StatusOK, gin.H{
			"code": 0, // 0表示"成功"（前端可根据code=0判断业务正常）
			"msg":  "注册成功，请登录",
			"data": gin.H{"username": newUser.Username}, // 返回用户名（供前端确认注册信息）
		})
	}
}

// Login 登录接口处理器
// @Summary 用户登录
// @Tags 认证
// @Accept json
// @Produce json
// @Param request body LoginRequest true "登录请求参数"
// @Success 200 {object} map[string]interface{} "{"code":0,"msg":"登录成功","data":{"token":"xxx","username":"xxx","email":"xxx@xxx.com"}}"
// @Failure 400 {object} map[string]interface{} "{"code":400,"msg":"参数错误"}"
// @Failure 500 {object} map[string]interface{} "{"code":500,"msg":"服务器错误（如数据库查询、Token生成失败）"}"
// @Router /auth/login [post]
// 功能：接收用户登录请求，校验用户名和密码，生成JWT Token返回（用于后续身份认证）
// 路由：POST /api/auth/login（公开接口，无需登录）
// 参数：db *gorm.DB - 数据库连接实例
// 返回：gin.HandlerFunc - Gin请求处理函数
func Login(db *gorm.DB) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 1. 绑定登录请求参数（用户名+密码）
		var req LoginRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"code": 400,
				"msg":  "参数错误：" + err.Error(),
			})
			return
		}

		// 2. 根据用户名查询用户（登录时用用户名作为唯一标识）
		var user model.User
		// Where条件：精确匹配用户名（用户名在注册时已保证唯一）
		if err := db.Where("username = ?", req.Username).First(&user).Error; err != nil {
			// 若用户不存在（ErrRecordNotFound）：返回"用户名或密码错误"（不明确区分，防止暴力破解）
			if err == gorm.ErrRecordNotFound {
				c.JSON(http.StatusOK, gin.H{
					"code": 1,
					"msg":  "用户名或密码错误",
				})
				return
			}
			// 数据库查询错误（如连接问题）
			c.JSON(http.StatusInternalServerError, gin.H{
				"code": 500,
				"msg":  "查询失败：" + err.Error(),
			})
			return
		}

		// 3. 验证密码（对比前端传入的明文密码与数据库中的哈希密码）
		// util.CheckPassword：将明文密码加密后与数据库哈希对比，返回布尔值
		if !util.CheckPassword(user.Password, req.Password) {
			// 密码不匹配：同样返回"用户名或密码错误"（安全考虑，不暴露具体错误项）
			c.JSON(http.StatusOK, gin.H{
				"code": 1,
				"msg":  "用户名或密码错误",
			})
			return
		}

		// 4. 生成JWT Token（登录成功的核心凭证）
		// util.GenerateToken：传入用户ID和用户名，生成包含用户身份的Token（有效期由配置决定）
		token, err := util.GenerateToken(user.ID, user.Username)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"code": 500,
				"msg":  "Token生成失败，请重试",
			})
			return
		}

		// 5. 登录成功：返回Token和用户基本信息（供前端存储和后续使用）
		c.JSON(http.StatusOK, gin.H{
			"code": 0,
			"msg":  "登录成功",
			"data": gin.H{
				"token":    token,         // JWT Token（前端需存储，后续请求需携带）
				"username": user.Username, // 用户名（前端显示用）
				"email":    user.Email,    // 邮箱（前端显示用，非敏感信息）
			},
		})
	}
}
